package AlgoritmosGeneticos.model.impl;


import java.util.List;

import utils.UI.JFreeChart.PlotHeaders;
import utils.UI.JFreeChart.UI;
import AlgoritmosGeneticos.model.AlgoritmoGenetico;
import AlgoritmosGeneticos.model.CondicionCorte;
import AlgoritmosGeneticos.model.EstrategiaDeSeleccion;
import AlgoritmosGeneticos.model.Individuo;
import AlgoritmosGeneticos.model.OperadorCruce;
import AlgoritmosGeneticos.model.OperadorMutacion;
import AlgoritmosGeneticos.model.Poblacion;
import AlgoritmosGeneticos.model.PoblacionSeleccionada;
import AlgoritmosGeneticos.model.impl.IndividuoImpl;
import AlgoritmosGeneticos.model.impl.PoblacionImpl;
import AlgoritmosGeneticos.model.impl.mutuaciones.MutacionNoUniforme;

public class AlgoritmoGeneticoImpl implements AlgoritmoGenetico{
	
	private int popSize;
	private int k;
	private double pMut;
	private int maxIterations;
	private fitness fitness;
	private OperadorMutacion opMut;
	private OperadorCruce opCru;
	private EstrategiaDeSeleccion eSeleccion;
	private EstrategiaDeSeleccion eReemplazo;
	private CondicionCorte condicion;
	private PlotHeaders headers;
	private Poblacion p;
	private boolean seePlot;
	
	/**
	 * 
	 * @param popSize		Tamaño de la población
	 * @param k				Tamaño de las selección
	 * @param pMut			Probabilidad de mutación de los individuos de la población
	 * 
	 */
	public AlgoritmoGeneticoImpl(int popSize, int k, double pMut) {
		if( popSize <= 0 || k <= 0 || k>popSize )
			throw new IllegalArgumentException("revise los parametros de la poblacion");
		if( pMut <0 || pMut > 1)
			throw new IllegalArgumentException("probabilidad incorrecta");
		
		this.popSize=popSize;
		this.k=k;
		this.pMut=pMut;
	}
	
	/**
	 *  ALGORITMO GENETICO
	 *  Repetir los siguientes pasos dada una condicion de corte 
	 *  en el ciclo de evolucion de una poblacion que se obtiene a 
	 *  través de los distintos parametros del algoritmo genetico
	 *  
	 *  1- Seleccion
	 *  2- Cruce
	 *  3- Mutacion
	 *  4- Reemplazo
	 *  
	 * @throws InterruptedException
	 */
	
	public void evolucionaPoblacion() throws InterruptedException{
		if(opMut == null){
			throw new IllegalArgumentException("Debe setear algun metodo de mutacion");
		}else if(opCru == null){
			throw new IllegalArgumentException("Debe setear algun metodo de cruce");
		}else if(eSeleccion == null){
			throw new IllegalArgumentException("Debe setear alguna estrategia de seleccion");
		}else if(eReemplazo == null){
			throw new IllegalArgumentException("Debe setear alguna estrategia de reemplazo");
		}else if(condicion == null){
			throw new IllegalArgumentException("Debe setear alguna estrategia de corte");
		}else if(seePlot && headers == null){
			throw new IllegalArgumentException("Si quiere ver una representacion grafica setee primeo los headers");
		}else if(fitness==AlgoritmoGenetico.fitness.DEFAULT)
			throw new IllegalArgumentException("Debe especificar una funcion de aptitud");

		p= new PoblacionImpl(popSize, k, pMut, fitness);
		
		int it=0;

		while(condicion.seCumple(p)){
//			System.out.println("ENTREEE!!!");
			PoblacionSeleccionada pobSel = p.seleccionar(eSeleccion);
			pobSel.cruzar(opCru);
			pobSel.mutar(opMut);
			p.reemplazar(pobSel, eReemplazo);

			if(seePlot){
				float[][] data= new float[2][popSize];
				if(it % 3 == 0){
					List<Individuo> poblacion = p.getIndividuos();
					for(int i=0; i<poblacion.size(); i++){
						data[0][i] = (float) poblacion.get(i).toNumber();
						data[1][i] = (float) PoblacionImpl.aptitud(poblacion.get(i));
					}
					UI.plot(headers,data);
					Thread.sleep(2000);
				}
			}
			if(opMut instanceof MutacionNoUniforme)
				((MutacionNoUniforme) opMut).increseIteration();
			it++;
		}
		System.out.println( "iteraciones: " + it);
		Individuo i = null; //este individuo deberia ser el optimo para la funcion dada, no se calcularlo
		System.out.println("Individuo Optimo: " + i );
		Individuo i2 = p.getIndividuos().get(0);
		double aptitudTotal = 0;
		for (Individuo ind : p.getIndividuos()) {
			if( PoblacionImpl.aptitud(i2) < PoblacionImpl.aptitud(ind)){
				i2 = ind;
			}
			aptitudTotal += PoblacionImpl.aptitud(ind);
		}
		System.out.println("Individuo mas apto de la poblacion: "+  i2.toNumber());
		System.out.println("Aptitud del individuo mas apto: " + PoblacionImpl.aptitud(i2));
		if(fitness.equals(AlgoritmoGenetico.fitness.MAXIMO))
			System.out.println("Diferencia de aptitud al optimo; " +  Math.abs((4.85891275076829 - PoblacionImpl.aptitud(i2))));
		else if(fitness.equals(AlgoritmoGenetico.fitness.MINIMO)){
			System.out.println("Diferencia de aptitud al optimo: " +  
					Math.abs((3.37264781546285 - (Math.sin(10*i2.toNumber())*Math.exp(-i2.toNumber()) + 4))));
		}
		System.out.println("Aptitud total de la poblacion: " + aptitudTotal);
	}
	
	

	public fitness getFitness() {
		return fitness;
	}

	public void setFitness(fitness fitness) {
		this.fitness= fitness;
	}

	public double getpMut() {
		return pMut;
	}

	public void setpMut(double pMut) {
		this.pMut = pMut;
	}

	public int getMaxIterations() {
		return maxIterations;
	}

	public void setMaxIterations(int maxIterations) {
		this.maxIterations = maxIterations;
	}

	public int getPopSize() {
		return popSize;
	}

	public void setPopSize(int popSize) {
		this.popSize = popSize;
	}

	public int getK() {
		return k;
	}

	public void setK(int k) {
		this.k = k;
	}

	public OperadorMutacion getOpMut() {
		return opMut;
	}

	public void setOpMut(OperadorMutacion opMut) {
		this.opMut = opMut;
	}

	public OperadorCruce getOpCru() {
		return opCru;
	}

	public void setOpCru(OperadorCruce opCru) {
		this.opCru = opCru;
	}

	public EstrategiaDeSeleccion geteSeleccion() {
		return eSeleccion;
	}

	public void seteSeleccion(EstrategiaDeSeleccion eSeleccion) {
		this.eSeleccion = eSeleccion;
	}

	public EstrategiaDeSeleccion geteReemplazo() {
		return eReemplazo;
	}

	public void seteReemplazo(EstrategiaDeSeleccion eReemplazo) {
		this.eReemplazo = eReemplazo;
	}

	public CondicionCorte getCondicion() {
		return condicion;
	}

	public void setCondicion(CondicionCorte condicion) {
		if (maxIterations == 0)
			throw new IllegalArgumentException("debe inicializar primero maxiterations");
		condicion.setMaxIterations(maxIterations);
		this.condicion = condicion;
	}

	public PlotHeaders getHeaders() {
		return headers;
	}

	public void setHeaders(PlotHeaders headers) {
		this.headers = headers;
	}

	public Poblacion getP() {
		return p;
	}

	public void setP(Poblacion p) {
		this.p = p;
	}

	public boolean isSeePlot() {
		return seePlot;
	}

	public void setSeePlot(boolean seePlot) {
		this.seePlot = seePlot;
	}
	
//	public static void main(String[] args) throws InterruptedException {
//		
//		int 	popSize=200;
//		int 	k=20;
//		double 	pMut=0.01;
//
//		new CustomRandom(1);
//		
//		OperadorMutacion 		opMut = new MutacionBasica();
//		OperadorCruce 			opCru = new CruceSimple();
//		EstrategiaDeSeleccion 	estrategiaSeleccion = new Elite();
//		EstrategiaDeSeleccion	estrategiaRemplazo = new Ruleta();
//		CondicionCorte			condicion = new CorteIteraciones(30);
//		PlotHeaders 			headers= new PlotHeaders();
//		
//		headers.setFunctionName("Function 1\n\nsin(10x) * exp(-x) - 4");
//		headers.setTitle("Grupo 6 - TPE 4");
//		headers.setXlabel("individuos");
//		headers.setYlabel("aptitud");
//		
//		Poblacion p= new PoblacionImpl(popSize, k, opMut, opCru, pMut);
//		
//		int it=0;
//		
//		while(condicion.seCumple(p)){
//			PoblacionSeleccionada pobSel = p.seleccionar(estrategiaSeleccion);
//			pobSel.cruzar(opCru);
//			pobSel.mutar(opMut);
//			p.reemplazar(pobSel, estrategiaRemplazo);
//			
//			float[][] data= new float[2][popSize];
//			if(it % 3 == 0){
//				List<Individuo> poblacion = p.getIndividuos();
//				for(int i=0; i<poblacion.size(); i++){
//					data[0][i] = (float) poblacion.get(i).toNumber();
//					data[1][i] = (float) PoblacionImpl.aptitud(poblacion.get(i));
//				}
//				UI.plot(headers,data);
//				Thread.sleep(2000);
//			}
//			if(opMut instanceof MutacionNoUniforme)
//				((MutacionNoUniforme) opMut).increseIteration();
//			it++;
//		}
//		
//	}
	
	
}
